package xyz.mumuwei.youthcourseserver.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import xyz.mumuwei.youthcourseserver.common.lang.Result;
import xyz.mumuwei.youthcourseserver.mapper.FileResourcesMapper;
import xyz.mumuwei.youthcourseserver.mapper.AvatarMapper;
import xyz.mumuwei.youthcourseserver.mapper.UserMapper;
import xyz.mumuwei.youthcourseserver.pojo.FileResources;
import xyz.mumuwei.youthcourseserver.pojo.User;
import xyz.mumuwei.youthcourseserver.pojo.UserAvatar;
import xyz.mumuwei.youthcourseserver.service.FileService;
import xyz.mumuwei.youthcourseserver.utils.FileUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

/**
 * @author mumuwei
 * @since 2022-02-19
 */
@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileResourcesMapper, FileResources> implements FileService {

    @Value("${filePath}")
    private String filePath;
    @Value("${avatarPath}")
    private String avatarPath;

    private UserMapper userMapper;
    private AvatarMapper avatarMapper;
    private FileResourcesMapper fileMapper;

    @Autowired
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Autowired
    public void setAvatarMapper(AvatarMapper avatarMapper) {
        this.avatarMapper = avatarMapper;
    }

    @Autowired
    public void setFileMapper(FileResourcesMapper fileMapper) {
        this.fileMapper = fileMapper;
    }

    /**
     * 上传用户账户头像
     *
     * @param file 头像文件
     * @param uid  用户id
     * @return 头像文件名
     */
    @Override
    public Result uploadAvatar(MultipartFile file, Integer uid)
            throws NoSuchAlgorithmException, IOException
    {
        // 获取文件校验码
        String checksum = FileUtil.extractChecksum(file, "SHA-256");
        // 检查系统是否已存储该文件
        UserAvatar avatar = avatarMapper.selectOne(new QueryWrapper<UserAvatar>()
                .eq("checksum", checksum));

        // 头像id
        Integer avatarId;

        String url = "";

        // 新上传文件不存在，则上传文件
        if (avatar == null)
        {
            // 获取文件名后缀，使用uuid生成新文件名
            String avatarName = file.getOriginalFilename();
            assert avatarName != null;
            String newFileName = createNewFileName(avatarName);

            // 设置文件上传路径，判断文件夹是否存在，不存在则创建文件夹
            String fileUrl = avatarPath + "/";
            if (!createMkdir(fileUrl))
            {
                log.error("创建文件夹失败！");
                return Result.fail("头像上传失败，服务器内部错误！");
            }

            // 创建文件输出流，存储文件
            saveFile(fileUrl + "/" + newFileName, file);

            // 存储文件信息
            UserAvatar userAvatar = new UserAvatar();
            userAvatar.setName(newFileName);
            url = newFileName;
            userAvatar.setChecksum(checksum);
            if (avatarMapper.insert(userAvatar) <= 0)
            {
                return Result.fail("头像上传失败，服务器内部错误！");
            }
            avatarId = userAvatar.getId();
        }
        else
        {
            // 新文件已存在，设置头像id
            avatarId = avatar.getId();
            url = avatar.getName();
        }

        // 更新用户信息
        User user = new User();
        user.setId(uid);
        user.setAvatarId(avatarId);
        if (userMapper.updateById(user) > 0)
        {
            return Result.success("头像上传成功！", url);
        }

        return Result.fail("头像上传失败，服务器内部错误！");
    }

    /**
     * @param file 上传的文件
     * @return 返回操作结果
     * @throws IOException IO操作异常
     * @throws NoSuchAlgorithmException 异常
     */
    @Override
    public Result uploadFile(MultipartFile file)
            throws IOException, NoSuchAlgorithmException
    {
        // 获取文件校验码
        String checksum = FileUtil.extractChecksum(file, "SHA-256");

        // 获取文件名，使用uuid生成新文件名
        String fileName = file.getOriginalFilename();

        // 创建文件对象
        FileResources resources = new FileResources();
        resources.setName(fileName);

        // 检查系统是否已存储该文件
        List<FileResources> fileList = fileMapper.selectList(
                new QueryWrapper<FileResources>()
                        .eq("checksum", checksum)
        );

        // 判断文件是否已存在
        if (fileList.size() == 0)
        {
            // 生成新的文件名
            assert fileName != null;
            String newFileName = createNewFileName(fileName);
            resources.setCode(newFileName);

            // 设置文件上传路径，按当前日期，生成文件夹路径
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            String format = simpleDateFormat.format(new Date());
            String fileUrl = filePath + "/" + format;
            resources.setUrl(format);

            // 创建文件夹
            if (!createMkdir(fileUrl)) {
                log.error("文件夹创建失败！");
                return Result.fail("文件上传失败，服务器内部错误");
            }

            // 存储文件信息
            saveFile(fileUrl + "/" + newFileName, file);
        }
        else
        {
            FileResources fileResources = fileList.get(0);
            // 文件已存在,只需添加记录
            resources.setCode(fileResources.getCode());
            resources.setUrl(fileResources.getUrl());
        }

        resources.setChecksum(checksum);
        int insert = fileMapper.insert(resources);
        if(insert > 0)
        {
            HashMap<String, Object> data = new HashMap<>(5);
            data.put("id", resources.getId());
            data.put("url", resources.getUrl() + "/" + resources.getCode());

            return Result.success("上传成功", data);
        }

        return Result.fail("文件上传失败，服务器内部错误");
    }

    /**
     * 使用UUID工具类创建新文件名
     * @param fileName 文件名
     * @return 新闻界名
     */
    private String createNewFileName(String fileName)
    {
        return UUID.randomUUID()
                .toString()
                .replaceAll("-", "") +
                fileName.substring(fileName.lastIndexOf("."));
    }

    /**
     * 判断文件夹是否存在
     * 不存在则创建文件夹
     * @param path 文件夹路径
     * @return true 文件夹存在或已创建
     */
    private Boolean createMkdir(String path)
    {
        // 判断文件夹是否存在，不存在则创建
        File targetFile = new File(path);
        if(!targetFile.exists()){
            return targetFile.mkdirs();
        }
        return true;
    }

    /**
     * 创建输出流，存储文件
     * @param path 文件存储路径
     * @param file 上传文件
     * @throws IOException IO操作异常
     */
    private void saveFile(String path, MultipartFile file) throws IOException
    {
        // 创建文件输出流，存储文件
        FileOutputStream fileOutputStream = new FileOutputStream(path);
        fileOutputStream.write(file.getBytes());
        fileOutputStream.close();
    }
}